package com.wang.template.json.asm;

import java.lang.reflect.Method;
import java.util.List;

import javax.lang.model.element.Modifier;

import org.mdkt.compiler.InMemoryJavaCompiler;

import com.squareup.javapoet.ArrayTypeName;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import com.wang.template.json.util.ReflectUtil;

@SuppressWarnings("restriction")
public class ProxyFactory {
	private static InMemoryJavaCompiler javac=InMemoryJavaCompiler.newInstance().ignoreWarnings();
	
	public static Class<?> createrProxyBean(Class<?> clazz) throws Exception {
		String sourceName = clazz.getSimpleName();
		String proxyClassName = sourceName + "Proxy";
		String proxyClassPackageName = clazz.getPackage().getName();
		
		// 修饰类
		TypeSpec.Builder proxyClassBuilder = TypeSpec
			.classBuilder(proxyClassName)
			.addModifiers(Modifier.PUBLIC)
			.addSuperinterface(ParameterizedTypeName.get(MethodAccess.class,clazz));
		// 增加bean方法存储数组
		String methodName="methodName";
		FieldSpec methodNameArray = FieldSpec
				.builder(ArrayTypeName.of(ClassName.get("java.lang", "String")),methodName)
				.addModifiers(Modifier.PRIVATE).build();
		proxyClassBuilder.addField(methodNameArray);

		FieldSpec methodParms = FieldSpec.builder(ArrayTypeName.of(ClassName.get("java.lang", "String")), "methodParms")
				.addModifiers(Modifier.PRIVATE).build();
		proxyClassBuilder.addField(methodParms);

		FieldSpec returnType = FieldSpec.builder(ArrayTypeName.of(ClassName.get("java.lang", "String")), "returnType")
				.addModifiers(Modifier.PRIVATE).build();
		proxyClassBuilder.addField(returnType);

		// invoke方法增加两个参数
		ParameterSpec sourceObjeParm = ParameterSpec
				.builder(ClassName.get(proxyClassPackageName, sourceName), "sourceObje").build();
		ParameterSpec methodNameParm = ParameterSpec.builder(ClassName.get("java.lang", "String"),methodName)
				.build();

		// invoke方法组装
		MethodSpec invoke = MethodSpec.methodBuilder("invoke").addModifiers(Modifier.PUBLIC)
				.returns(Object.class)
				.addParameter(sourceObjeParm)
				.addParameter(methodNameParm)
				.addCode(buildInvokeMethod(clazz,methodName))
				.build();
		
		TypeSpec proxyClass = proxyClassBuilder.addMethod(invoke).build();

		JavaFile javaFile = JavaFile.builder(proxyClassPackageName, proxyClass).build();
		//System.out.println(javaFile.toString());
		return javac.compile(proxyClassPackageName+"."+proxyClassName, javaFile.toString());
	}

	private static CodeBlock buildInvokeMethod(Class<?> clazz, String methodName, Object... parms) {
		CodeBlock.Builder code = CodeBlock.builder(); 
		code.beginControlFlow("switch($L)", methodName);
		List<Method> methodList = ReflectUtil.getAllMethod(clazz);
		for (Method method : methodList) {
			String innerMethodName = method.getName();
			if (innerMethodName.startsWith("get")) {
				if (method.getReturnType() == Void.TYPE) {
					code.add("case  \"$N\":{sourceObje.$N();return 1;}\n", innerMethodName, innerMethodName);
				} else {
					code.add("case  \"$N\":{return sourceObje.$N();}\n", innerMethodName, innerMethodName);
				}
			} else if (methodName.startsWith("set")) {
				// code.add("case \"$N\":{sourceObje.$N($N);break;}\n", methodName,methodName,);
			}
		}
		code.add("default: throw new RuntimeException(\"$T对象不存在方法\"+$N);",clazz,methodName);
		code.endControlFlow();
		return code.build();
	}
}
